#pragma once
#include <atomic>
#include <mutex>
#include <cstdarg>
#include <cstdio>
#include <sstream>
#include <memory>
#include <unordered_map>
#include "Util.hpp"
#include "Message.hpp"
#include "Level.hpp"
#include "Format.hpp"
#include "Sink.hpp"
#include "Looper.hpp"
namespace mylog
{
    class Logger
    {
    public:
        using ptr = std::shared_ptr<Logger>;
        Logger(const std::string &logger_name,
               LogLevel::value level,
               Formatter::ptr &formatter,
               std::vector<Logsink::ptr> &sink)
            : _logger_name(logger_name),
              _limit_level(level),
              _formatter(formatter),
              _sinks(sink.begin(), sink.end()) {}
        void debug(const std::string &file, size_t line, const std::string &fmt, ...)
        {
            if (_limit_level > LogLevel::value::DEBUG)
                return;
            va_list ls;
            va_start(ls, fmt);
            char *res; // 需要手动释放
            int ret = vasprintf(&res, fmt.c_str(), ls);
            if (ret == -1)
            {
                std::cout << "vasprintf error" << std::endl;
                free(res);
                return;
            }
            va_end(ls);
            serialize(LogLevel::value::DEBUG, line, file, res);
            free(res);
        }
        void info(const std::string &file, size_t line, const std::string &fmt, ...)
        {
            if (_limit_level > LogLevel::value::INFO)
                return;
            va_list ls;
            va_start(ls, fmt);
            char *res; // 需要手动释放
            int ret = vasprintf(&res, fmt.c_str(), ls);
            if (ret == -1)
            {
                std::cout << "vasprintf error" << std::endl;
                free(res);
                return;
            }
            va_end(ls);
            serialize(LogLevel::value::INFO, line, file, res);
            free(res);
        }
        void warn(const std::string &file, size_t line, const std::string &fmt, ...)
        {
            if (_limit_level > LogLevel::value::WARN)
                return;
            va_list ls;
            va_start(ls, fmt);
            char *res; // 需要手动释放
            int ret = vasprintf(&res, fmt.c_str(), ls);
            if (ret == -1)
            {
                std::cout << "vasprintf error" << std::endl;
                free(res);
                return;
            }
            va_end(ls);
            serialize(LogLevel::value::WARN, line, file, res);
            free(res);
        }
        void error(const std::string &file, size_t line, const std::string &fmt, ...)
        {
            if (_limit_level > LogLevel::value::ERROR)
                return;
            va_list ls;
            va_start(ls, fmt);
            char *res; // 需要手动释放
            int ret = vasprintf(&res, fmt.c_str(), ls);
            if (ret == -1)
            {
                std::cout << "vasprintf error" << std::endl;
                free(res);
                return;
            }
            va_end(ls);
            serialize(LogLevel::value::ERROR, line, file, res);
            free(res);
        }
        void fatal(const std::string &file, size_t line, const std::string &fmt, ...)
        {
            if (_limit_level > LogLevel::value::FATAL)
                return;
            va_list ls;
            va_start(ls, fmt);
            char *res; // 需要手动释放
            int ret = vasprintf(&res, fmt.c_str(), ls);
            if (ret == -1)
            {
                std::cout << "vasprintf error" << std::endl;
                free(res);
                return;
            }
            va_end(ls);
            serialize(LogLevel::value::FATAL, line, file, res);
            free(res);
        }
        const std::string& name()
        {
            return _logger_name;
        }
    protected:
        void serialize(LogLevel::value level, size_t line, const std::string &file, const char *res)
        {

            LogMessage msg(level, line, file, _logger_name, res);
            std::stringstream ss;
            _formatter->Format(ss, msg);
            Log(ss.str().c_str(), ss.str().size());
        }
        virtual void Log(const char *data, size_t len) = 0;

    protected:
        std::mutex _mutex;
        std::string _logger_name;
        std::atomic<LogLevel::value> _limit_level;
        Formatter::ptr _formatter;
        std::vector<Logsink::ptr> _sinks;
    };
    class SyncLogger : public Logger
    {
    public:
        SyncLogger(const std::string &logger_name,
                   LogLevel::value level,
                   Formatter::ptr &formatter,
                   std::vector<Logsink::ptr> &sink)
            : Logger(logger_name, level, formatter, sink) {}

    protected:
        void Log(const char *data, size_t len)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            if (_sinks.empty())
                return;
            for (auto &sink : _sinks)
            {
                sink->Log(data, len);
            }
        }
    };
    class AsyncLogger : public Logger
    {
    public:
        AsyncLogger(const std::string &logger_name,
                   LogLevel::value level,
                   Formatter::ptr &formatter,
                   std::vector<Logsink::ptr> &sink,
                   AsyncType looper_type)
            : Logger(logger_name, level, formatter, sink),
              _looper(std::make_shared<AsyncLooper>(std::bind(&AsyncLogger::ReadLog, this, std::placeholders::_1), looper_type)) {}
        void Log(const char *data, size_t len)
        {
            _looper->Push(data, len);
        }
        void ReadLog(Buffer &buffer)
        {
            if(_sinks.empty()) return;
            for(auto& sink : _sinks)
            {
                sink->Log(buffer.Begin(), buffer.ReadableSize());
            }
        }

    protected:
        AsyncLooper::ptr _looper;
    };
    enum class LoggerType
    {
        LOGGER_SYNC,
        LOGGER_ASYNC
    };
    class LoggerBuilder
    {
    public:
        LoggerBuilder()
            : _logger_type(LoggerType::LOGGER_SYNC),
              _limit_level(LogLevel::value::DEBUG),
              _looper_type(AsyncType::ASYNC_SAFE) {}
        void BuildLoggerType(LoggerType type)
        {
            _logger_type = type;
        }
        void BuildENableUnsafe()
        {
            _looper_type = AsyncType::ASYNC_UNSAFE;
        }
        void BuildLoggerName(const std::string &name)
        {
            _logger_name = name;
        }
        void BuildLoggerLevel(LogLevel::value level)
        {
            _limit_level = level;
        }
        void BuildFormatter(const std::string &pattern)
        {
            _formatter = std::make_shared<Formatter>(pattern);
        }
        template <typename SinkType, typename... Args>
        void BuildSink(Args &&...args)
        {
            Logsink::ptr psink = SinkFactory::CreateSink<SinkType>(std::forward<Args>(args)...);
            _sinks.push_back(psink);
        }
        virtual Logger::ptr Build() = 0;

    protected:
        LoggerType _logger_type;
        std::string _logger_name;
        LogLevel::value _limit_level;
        Formatter::ptr _formatter;
        std::vector<Logsink::ptr> _sinks;
        AsyncType _looper_type;
    };
    class LocalLoggerBuilder : public LoggerBuilder
    {
    public:
        Logger::ptr Build() override
        {
            assert(!_logger_name.empty());
            if (_formatter.get() == nullptr)
            {
                _formatter = std::make_shared<Formatter>();
            }
            if (_sinks.empty())
            {
                BuildSink<StdoutSink>();
            }
            if (_logger_type == LoggerType::LOGGER_ASYNC)
            {
                return std::make_shared<AsyncLogger>(_logger_name, _limit_level, _formatter, _sinks, _looper_type);
            }
            return std::make_shared<SyncLogger>(_logger_name, _limit_level, _formatter, _sinks);
        }
    };
    class LoggerManarge
    {
    public:
        static LoggerManarge& GetInstance()
        {
            static LoggerManarge eton;
            return eton;
        }
        void AddLogger(Logger::ptr& logger)
        {
            if(HasLogger(logger->name())) return;
            std::unique_lock<std::mutex> lock(_mutex);
            _loggers.insert(std::make_pair(logger->name(), logger));
        }
        bool HasLogger(const std::string& name)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            auto it = _loggers.find(name);
            if(it == _loggers.end())
            {
                return false;
            }
            return true;
        }
        Logger::ptr GetLogger(const std::string& name)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            auto it = _loggers.find(name);
            if(it == _loggers.end())
            {
                return nullptr;
            }
            return it->second;
        }
        Logger::ptr RootLogger()
        {
            return _root_logger;
        }
    protected:
        LoggerManarge()
        {
            std::unique_ptr<LoggerBuilder> budder(new LocalLoggerBuilder());
            budder->BuildLoggerName("root");
            _root_logger = budder->Build();
            _loggers.insert(std::make_pair("root", _root_logger));
        }
    protected:
        std::mutex _mutex;
        Logger::ptr _root_logger;
        std::unordered_map<std::string, Logger::ptr> _loggers;
    };
    class GlobalLoggerBuilder : public LoggerBuilder
    {
    public:
        Logger::ptr Build() override
        {
            assert(!_logger_name.empty());
            if (_formatter.get() == nullptr)
            {
                _formatter = std::make_shared<Formatter>();
            }
            if (_sinks.empty())
            {
                BuildSink<StdoutSink>();
            }
            Logger::ptr logger;
            if (_logger_type == LoggerType::LOGGER_ASYNC)
            {
                logger = std::make_shared<AsyncLogger>(_logger_name, _limit_level, _formatter, _sinks, _looper_type);
            }
            else
            {
                logger = std::make_shared<SyncLogger>(_logger_name, _limit_level, _formatter, _sinks);
            }
            LoggerManarge::GetInstance().AddLogger(logger);
            return logger;
        }
    };
}